package com.xinfeng.commons.math;

import java.text.DecimalFormat;

public class BinaryMath {
	
	/**
	 * 计算出N个因素在顺序不变的前提下能组
	 * 合出多少种可能
	 * @param arrayLength 因素数目
	 * @return int 能组合的最大可能数
	 */
	public int computeMaxValue(int arrayLength){
		int result = 0;
		
		/*
		 * 如因素数目arrayLength值为n，那么每一个因素在组合中有两种
		 * 状态有和没有（即可用1和0表示），那么n个因素组合表示可写成
		 * 如下：
		 * f1, f2, f3, ... , fn = f(n - (n - 1)), f(n - (n - 2)), ... , f(n - (n - n))
		 * 以上的1, 2, 3, ..., n表示的是f的下标，(n - (n - 1))也是f的
		 * 下标.那么每一个f只有两种值0和1，也即是二进制数。
		 * 假设n存在着一个最在可能组合数为M那么该值就是等于由n位二进数
		 * 表示的最大值加1, 假设M=16=(1111b) + 1，因此n=4，现是已知n
		 * 为arrayLength，可计算出1111b的十进制数
		 */
		for(int i = 0; i < arrayLength; i++){
			result += new Double(Math.pow(2, i)).intValue();
		}
		
		//n位二进数表示的最大值加1
		return result + 1;
	}
	
	/**
	 * 为了格式化数值转化为字符串的格串，定义格式化
	 * 如：10，的数值要转化为0010，那么，字符格式即
	 * 为0000，即当10转为字符后不够4个字符，向前补0
	 * @param len将要定义的格式的字符串长度
	 * @return 字符串格式
	 */
	public String getMyFormatString(int len){
		String result = "";
		
		for(int i = 0; i < len; i++){
			result = result.concat("0");
		}
		
		return result;
	}
	
	/**
	 * 提取出len个因素的第number个组合
	 * @param number 组合的索引从1开始
	 * @param len 组合的组成因素的个数
	 * @return int[] 以二进制表示的组合，其长度与len相等，int[n]={0,1}
	 * 0和1表示组合时一个因素的状没有和有
	 */
	public int[] takeCombination(int number, int len) {
		int[] result = new int[len];
		
		if(number > 0 && len > 0){
			int maxValue = computeMaxValue(len); //根据因素的个数计算出最大可能的组合数
			
			if(number <= maxValue){ //组合的索引不能超过最大组合数
				DecimalFormat format = new DecimalFormat(getMyFormatString(len));
				
				/*
				 * number索引值即是一种组合的表示，把其转化为更直观和可使用的方式，
				 * 1、组合的方式的范围0到最大组合数减1，而number的索引值从1开始，所
				 * 以在对number转化时先减1，把number - 1的值转变为以二进制表示,并以
				 * 的字符串表示，再把字符串转化为数字，为是可以方便字串格式数字，格式
				 * 为了在指定的位数为够了能被补零，二进制的字符串其长度必需和传入的因
				 * 素个数相等，因为其每一位都表示一个因素的状态
				 */
				String str = Integer.toBinaryString(number - 1);
				int iVal = Integer.parseInt(str);
				str = format.format(iVal);
				
				//将二进制，转化成更方便的计算方式，如"1111"转
				//化为int[0]=1, int[1]=1, int[2]=1, int[3]=1
				for(int i = 0 ; i < str.length(); i++){
					char c = str.charAt(i);
					
					if('0' == c){
						result[i] = 0;
					}else{
						result[i] = 1;
					}
				}
			}else{
				throw new IllegalArgumentException("组合的索引值大于最大可组合的数量！");
			}
		}else{
			throw new IllegalArgumentException("组合的索引值或组合的个数不能为零！");
		}
		
		return result;
	}
	
	/**
	 * 计算出N个因素所有的组合，在顺序不变的前提
	 * @param array 因素
	 * @return String[][] 所有组合
	 */
	public String[][] assemble(String[] array){
		String[][] result = null;
		
		if(array != null && array.length > 0){
			int len = array.length;
			int maxValue = computeMaxValue(len); //计算出最大的可组合数
			result = new String[maxValue][len];
			int index = 0;
			
			//循环提取出array因素所有组合
			for(int i = 1; i <= maxValue; i++, index++){
				int[] combination = takeCombination(i, len);
				
				for(int j = 0; j < len; j++){
					if(combination[j] == 1){ //0表示第j个因素在组合是不是参与
						result[index][j] = array[j]; //有
					}else{
						result[index][j] = "-"; //没有
					}
				}
			}
		}
		
		return result;
	}
	
	/**
	 * 显示一个二维护字符串数组
	 * @param tArray
	 */
	public void show(String[][] tArray){
		for(int i = 0; i < tArray.length; i++){
			System.out.println();
			
			for(int j = 0; j < tArray[i].length; j++){
				System.out.print(tArray[i][j]);
				
				if(j + 1 < tArray[i].length){
					System.out.print(", ");
				}
			}
		}
	}

}
